package com.xm.bluetoothkotlin.connect

import android.annotation.SuppressLint
import android.bluetooth.*
import android.bluetooth.BluetoothDevice.TRANSPORT_LE
import android.os.*
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ThreadUtils
import com.xm.bluetoothkotlin.BluetoothManager
import com.xm.bluetoothkotlin.connect.back.*
import com.xm.bluetoothkotlin.constant.*
import com.xm.bluetoothkotlin.device.BleConnectStateParameter
import com.xm.bluetoothkotlin.device.BluetoothDeviceData
import java.lang.Exception

/**
 * @创建者:小垚
 * @时间:2021/11/15
 *@描述:
 */
class BleBluetooth(deviceData: BluetoothDeviceData) {
    private var bleGattCallback: BluetoothConnectCallback? = null
    private var bleRssiCallback: BleRssiCallback? = null
    private var bleMtuChangedCallback: BleMtuCallback? = null

    private var bleNotifyCallbackHashMap = mutableMapOf<String, BleNotifyCallback>()
    private var bleIndicateCallbackHashMap = mutableMapOf<String, BleIndicateCallback>()
    private var bleWriteCallbackHashMap = mutableMapOf<String, BleWriteCallback>()
    private var bleReadCallbackHashMap = mutableMapOf<String, BleReadCallback>()

    private var lastState = LastState.CONNECT_IDLE
    private var isActiveDisconnect = false
    var device: BluetoothDeviceData = deviceData
    var bluetoothGatt: BluetoothGatt? = null
    var config: ConnectConfigData? = null


    fun newBleConnector() = BluetoothConnect(this)

    /**
     * 设置连接回调
     */
    fun addConnectGattCallback(callback: BluetoothConnectCallback) {
        bleGattCallback = callback
    }

    /**
     * 移除连接回调
     */
    fun removeConnectGattCallback() {
        bleGattCallback = null
    }

    /**
     * 新增信号回调
     */
    fun addRssiCallback(callback: BleRssiCallback) {
        bleRssiCallback = callback
    }

    /**
     * 移除信号回调
     */
    fun removeRssiCallback() {
        bleRssiCallback = null
    }

    /**
     * 新增信号回调
     */
    fun addMtuChangedCallback(callback: BleMtuCallback) {
        bleMtuChangedCallback = callback
    }

    /**
     * 移除信号回调
     */
    fun removeMtuChangedCallback() {
        bleMtuChangedCallback = null
    }


    /**
     * 新增通知回调
     */
    fun addNotifyCallback(uuid: String, callback: BleNotifyCallback) {
        if (!bleNotifyCallbackHashMap.containsKey(uuid)) {
            bleNotifyCallbackHashMap[uuid] = callback
        }
    }

    /**
     * 移除通知回调
     */
    fun removeNotifyCallback(uuid: String) {
        if (bleNotifyCallbackHashMap.containsKey(uuid)) {
            bleNotifyCallbackHashMap.remove(uuid)
        }
    }

    /**
     * 新增Indicate回调
     */
    fun addIndicateCallback(uuid: String, callback: BleIndicateCallback) {
        if (!bleIndicateCallbackHashMap.containsKey(uuid)) {
            bleIndicateCallbackHashMap[uuid] = callback
        }
    }

    /**
     * 移除Indicate回调
     */
    fun removeIndicateCallback(uuid: String) {
        if (bleIndicateCallbackHashMap.containsKey(uuid)) {
            bleIndicateCallbackHashMap.remove(uuid)
        }
    }

    /**
     * 新增输入回调
     */
    fun addWriteCallback(uuid: String, callback: BleWriteCallback) {
        if (!bleWriteCallbackHashMap.containsKey(uuid)) {
            bleWriteCallbackHashMap[uuid] = callback
        }
    }

    /**
     * 移除输入回调
     */
    fun removeWriteCallback(uuid: String) {
        if (bleWriteCallbackHashMap.containsKey(uuid)) {
            bleWriteCallbackHashMap.remove(uuid)
        }
    }

    /**
     * 新增读取回调
     */
    fun addReadCallback(uuid: String, callback: BleReadCallback) {
        if (!bleReadCallbackHashMap.containsKey(uuid)) {
            bleReadCallbackHashMap[uuid] = callback
        }
    }

    /**
     * 移除读取回调
     */
    fun removeReadCallback(uuid: String) {
        if (bleReadCallbackHashMap.containsKey(uuid)) {
            bleReadCallbackHashMap.remove(uuid)
        }
    }

    /**
     * 清空所有回调
     */
    fun clearCharacterCallback() {
        bleNotifyCallbackHashMap.clear()
        bleIndicateCallbackHashMap.clear()
        bleWriteCallbackHashMap.clear()
        bleReadCallbackHashMap.clear()
    }

    /**
     * 连接设备
     */
    @SuppressLint("MissingPermission")
    fun connect(config: ConnectConfigData, callback: BluetoothConnectCallback): BluetoothGatt {
        LogUtils.e("connect device: ${device.name} \n mac:${device.address} \n config:${config.toString()}")
        this.config = config

        addConnectGattCallback(callback)
        lastState = LastState.CONNECT_CONNECTING
        bluetoothGatt = if (device.device == null) {
            val bluetoothDevice = BluetoothManager.bluetoothAdapter.getRemoteDevice(device.address)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                bluetoothDevice.connectGatt(BluetoothManager.context, config.isAgain, coreGattCallback, TRANSPORT_LE)
            } else {
                bluetoothDevice.connectGatt(BluetoothManager.context, config.isAgain, coreGattCallback)
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                device.device!!.connectGatt(BluetoothManager.context, config.isAgain, coreGattCallback, TRANSPORT_LE)
            } else {
                device.device!!.connectGatt(BluetoothManager.context, config.isAgain, coreGattCallback)
            }
        }

        if (bluetoothGatt != null) {
            bleGattCallback?.run {
                onStartConnect()
            }
            var message = mainHandler.obtainMessage()
            message.what = MSG_CONNECT_OVER_TIME
            mainHandler.sendMessageDelayed(message, config.connectTime)
        } else {
            disconnectGatt()
            refreshDeviceCache()
            closeBluetoothGatt()
            lastState = LastState.CONNECT_FAILURE
            BluetoothManager.removeConnecting(this)
            bleGattCallback?.run {
                onConnectFail(this@BleBluetooth, "onConnectFail==null")
            }
        }
        return bluetoothGatt!!
    }

    /**
     * 主动断开设备
     */
    fun disconnect() {
        isActiveDisconnect = true
        disconnectGatt()
    }

    /**
     * 释放资源
     */
    fun destroy() {
        lastState = LastState.CONNECT_IDLE
        disconnectGatt()
        refreshDeviceCache()
        closeBluetoothGatt()
        removeConnectGattCallback()
        removeRssiCallback()
        removeMtuChangedCallback()
        clearCharacterCallback()
        mainHandler.removeCallbacksAndMessages(null)
    }


    private fun closeBluetoothGatt() {
        bluetoothGatt?.run {
            close()
        }
    }


    private fun refreshDeviceCache() {
        try {
            val refresh = BluetoothGatt::class.java.getMethod("refresh")
            if (refresh != null && bluetoothGatt != null) {
                val success = refresh.invoke(bluetoothGatt) as Boolean
                LogUtils.e("refreshDeviceCache, is success:  $success")
            }
        } catch (e: Exception) {
            LogUtils.e("exception occur while refreshing device: " + e.message)
            e.printStackTrace()
        }
    }


    @SuppressLint("MissingPermission")
    private fun disconnectGatt() {
        bluetoothGatt?.run {
            disconnect()
        }
    }


    private val mainHandler by lazy {
        object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    MSG_CONNECT_FAIL -> {
                        //连接失败
                        disconnectGatt()
                        refreshDeviceCache()
                        closeBluetoothGatt()

                        config?.run {
                            if (connectCount < count) {
                                LogUtils.e("重连")
                                connectCount++
                                val message = obtainMessage()
                                message.what = MSG_RECONNECT
                                sendMessageDelayed(message, countTime)
                            } else {
                                lastState = LastState.CONNECT_FAILURE
                                BluetoothManager.removeConnecting(this@BleBluetooth)
                                var para = msg.obj as BleConnectStateParameter
                                bleGattCallback?.run {
                                    onConnectFail(this@BleBluetooth, "失败原因：${para.toString()}")
                                }
                            }
                        }
                    }

                    MSG_DISCONNECTED -> {
                        //断开连接
                        lastState = LastState.CONNECT_DISCONNECT
                        BluetoothManager.removeConnecting(this@BleBluetooth)

                        disconnectGatt()
                        refreshDeviceCache()
                        closeBluetoothGatt()
                        removeMtuChangedCallback()
                        clearCharacterCallback()
                        removeCallbacksAndMessages(null)

                        var para = msg.obj as BleConnectStateParameter
                        bleGattCallback?.run {
                            onDisConnected(para.isActive, this@BleBluetooth, bluetoothGatt!!, para.status)
                        }
                    }

                    MSG_RECONNECT -> {
                        //重新连接
                        bleGattCallback?.run {
                            connect(config!!, this)
                        }
                    }

                    MSG_CONNECT_OVER_TIME -> {
                        //连接超时
                        disconnectGatt()
                        refreshDeviceCache()
                        closeBluetoothGatt()

                        lastState = LastState.CONNECT_FAILURE
                        BluetoothManager.removeConnecting(this@BleBluetooth)

                        bleGattCallback?.run {
                            onConnectFail(this@BleBluetooth, "连接超时")
                        }
                    }

                    MSG_DISCOVER_SERVICES -> {
                        //连接中
                        if (bluetoothGatt != null) {
                            val discoverServiceResult = bluetoothGatt!!.discoverServices()
                            if (!discoverServiceResult) {
                                val message = obtainMessage()
                                message.what = MSG_DISCOVER_FAIL
                                sendMessage(message)
                            }
                        } else {
                            val message = obtainMessage()
                            message.what = MSG_DISCOVER_FAIL
                            sendMessage(message)
                        }
                    }

                    MSG_DISCOVER_FAIL -> {
                        //连接失败
                        disconnectGatt()
                        refreshDeviceCache()
                        closeBluetoothGatt()

                        lastState = LastState.CONNECT_FAILURE
                        BluetoothManager.removeConnecting(this@BleBluetooth)
                        bleGattCallback?.run {
                            onConnectFail(this@BleBluetooth, "其他异常")
                        }
                    }

                    MSG_DISCOVER_SUCCESS -> {
                        //连接成功
                        lastState = LastState.CONNECT_CONNECTED
                        isActiveDisconnect = false
                        BluetoothManager.removeConnecting(this@BleBluetooth)
                        BluetoothManager.addConnecting(this@BleBluetooth)

                        config?.run {
                            connectCount = 0
                        }
                        var para = msg.obj as BleConnectStateParameter
                        bleGattCallback?.run {
                            onConnectSuccess(this@BleBluetooth, bluetoothGatt!!, para.status)
                        }
                    }
                    else -> {
                        super.handleMessage(msg)
                    }
                }
            }
        }
    }


    private val coreGattCallback by lazy {
        object : BluetoothGattCallback() {
            override fun onPhyUpdate(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
                LogUtils.e("onPhyUpdate")
                super.onPhyUpdate(gatt, txPhy, rxPhy, status)
            }

            override fun onPhyRead(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
                LogUtils.e("onPhyRead")
                super.onPhyRead(gatt, txPhy, rxPhy, status)
            }

            override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                LogUtils.e("onConnectionStateChange")
                super.onConnectionStateChange(gatt, status, newState)
                //设备状态变更回调
                LogUtils.e("BluetoothGattCallback:onConnectionStateChange -> status:${status} newState:${newState}")

                bluetoothGatt = gatt

                mainHandler.removeMessages(MSG_CONNECT_OVER_TIME)

                when (newState) {
                    BluetoothProfile.STATE_CONNECTED -> {
                        val message = mainHandler.obtainMessage()
                        message.what = MSG_DISCOVER_SERVICES
                        mainHandler.sendMessageDelayed(message, 500)
                    }

                    BluetoothProfile.STATE_DISCONNECTED -> {
                        if (lastState == LastState.CONNECT_CONNECTING) {
                            val message = mainHandler.obtainMessage()
                            message.what = MSG_CONNECT_FAIL
                            message.obj = BleConnectStateParameter(status)
                            mainHandler.sendMessage(message)
                        } else {
                            val message = mainHandler.obtainMessage()
                            message.what = MSG_DISCONNECTED
                            val para = BleConnectStateParameter(status, isActiveDisconnect)
                            message.obj = para
                            mainHandler.sendMessage(message)
                        }
                    }
                    else -> {

                    }
                }

            }

            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
                LogUtils.e("onServicesDiscovered")
                super.onServicesDiscovered(gatt, status)
                LogUtils.e("BluetoothGattCallback：onServicesDiscovered -> status:${status}")
                bluetoothGatt = gatt

                if (status == BluetoothGatt.GATT_SUCCESS) {
                    val message = mainHandler.obtainMessage()
                    message.what = MSG_DISCOVER_SUCCESS
                    message.obj = BleConnectStateParameter(status)
                    mainHandler.sendMessage(message)
                } else {
                    val message = mainHandler.obtainMessage();
                    message.what = MSG_DISCOVER_FAIL;
                    mainHandler.sendMessage(message)
                }
            }

            override fun onCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
                LogUtils.e("onCharacteristicRead")
                super.onCharacteristicRead(gatt, characteristic, status)

                characteristic?.run {
                    bleReadCallbackHashMap.keys.forEachIndexed { _, key ->
                        if (uuid.toString() == key) {
                            var callback = bleReadCallbackHashMap[key]

                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_READ_RESULT
                                    obj = callback
                                    data = Bundle().apply {
                                        putInt(KEY_READ_BUNDLE_STATUS, status)
                                        putByteArray(KEY_READ_BUNDLE_VALUE, characteristic.value)
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }
                }

            }

            override fun onCharacteristicWrite(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
                LogUtils.e("onCharacteristicWrite")
                super.onCharacteristicWrite(gatt, characteristic, status)

                characteristic?.run {
                    bleWriteCallbackHashMap.keys.forEachIndexed { _, key ->
                        if (uuid.toString() == key) {
                            var callback = bleWriteCallbackHashMap[key]

                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_WRITE_RESULT
                                    obj = callback
                                    data = Bundle().apply {
                                        putInt(KEY_WRITE_BUNDLE_STATUS, status)
                                        putByteArray(KEY_WRITE_BUNDLE_VALUE, characteristic.value)
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }
                }
            }

            override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
                LogUtils.e("onCharacteristicChanged")
                super.onCharacteristicChanged(gatt, characteristic)
                characteristic?.run {
                    bleNotifyCallbackHashMap.keys.forEachIndexed { index, key ->
                        if (uuid.toString() == key) {
                            var callback = bleNotifyCallbackHashMap[key]
                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_NOTIFY_DATA_CHANGE
                                    obj = callback
                                    data = Bundle().apply {
                                        putByteArray(KEY_NOTIFY_BUNDLE_VALUE, characteristic.value)
                                        putString(KEY_NOTIFY_BUNDLE_UUID_VALUE,characteristic.uuid.toString())
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }

                    bleIndicateCallbackHashMap.keys.forEachIndexed { _, key ->
                        if (uuid.toString() == key) {
                            var callback = bleIndicateCallbackHashMap[key]
                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_INDICATE_DATA_CHANGE
                                    obj = callback
                                    data = Bundle().apply {
                                        putByteArray(KEY_INDICATE_BUNDLE_VALUE, characteristic.value)
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }

                }
            }

            override fun onDescriptorRead(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int) {
                LogUtils.e("onDescriptorRead")
                super.onDescriptorRead(gatt, descriptor, status)
            }

            override fun onDescriptorWrite(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int) {
                LogUtils.e("onDescriptorWrite")
                super.onDescriptorWrite(gatt, descriptor, status)

                descriptor?.run {
                    bleNotifyCallbackHashMap.keys.forEachIndexed { _, key ->
                        if (uuid.toString() == key) {
                            var callback = bleNotifyCallbackHashMap[key]

                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_NOTIFY_RESULT
                                    obj = callback
                                    data = Bundle().apply {
                                        putInt(KEY_NOTIFY_BUNDLE_STATUS, status)
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }


                    bleIndicateCallbackHashMap.keys.forEachIndexed { _, key ->
                        if (uuid.toString() == key) {
                            var callback = bleIndicateCallbackHashMap[key]

                            callback?.handler?.run {
                                val message = obtainMessage().apply {
                                    what = MSG_CHA_INDICATE_RESULT
                                    obj = callback
                                    data = Bundle().apply {
                                        putInt(KEY_INDICATE_BUNDLE_STATUS, status)
                                    }
                                }
                                sendMessage(message)
                            }
                        }
                    }
                }
            }

            override fun onReliableWriteCompleted(gatt: BluetoothGatt?, status: Int) {
                LogUtils.e("onReliableWriteCompleted")
                super.onReliableWriteCompleted(gatt, status)
            }

            override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
                LogUtils.e("onReadRemoteRssi")
                super.onReadRemoteRssi(gatt, rssi, status)
                bleRssiCallback?.handler?.run {
                    sendMessage(obtainMessage().apply {
                        what = MSG_READ_RSSI_RESULT
                        obj = bleRssiCallback
                        data = Bundle().apply {
                            putInt(KEY_READ_RSSI_BUNDLE_STATUS, status)
                            putInt(KEY_READ_RSSI_BUNDLE_VALUE, rssi)
                        }
                    })
                }
            }

            override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
                LogUtils.e("onMtuChanged")
                super.onMtuChanged(gatt, mtu, status)

                bleMtuChangedCallback?.handler?.run {
                    sendMessage(obtainMessage().apply {
                        what = MSG_SET_MTU_RESULT
                        obj = bleMtuChangedCallback
                        data = Bundle().apply {
                            putInt(KEY_SET_MTU_BUNDLE_STATUS, status)
                            putInt(KEY_SET_MTU_BUNDLE_VALUE, mtu)
                        }
                    })
                }
            }

            override fun onServiceChanged(gatt: BluetoothGatt) {
                LogUtils.e("onServiceChanged")
                super.onServiceChanged(gatt)
            }
        }
    }

}

enum class LastState {
    CONNECT_IDLE, //连接空闲
    CONNECT_CONNECTING, //正在连接
    CONNECT_CONNECTED,//已连接
    CONNECT_FAILURE, //连接失败
    CONNECT_DISCONNECT //断开连接
}